﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEditor;
using UnityEngine;

public class ProjectReferenceThread : EditorWindow
{
    public static readonly int taskCount = SystemInfo.processorCount * 8;

    [MenuItem("Assets/Find Reference In Project", false, 20)]
    private static void FindReferenceInProject()
    {
        SerachAssetRefByGuid(Selection.activeObject);
    }

    private static void SerachAssetRefByGuid(UnityEngine.Object asset)
    {
        if (null == asset)
        {
            return;
        }

        //Collect all assets.
        var fileList = GetAllFiles(Application.dataPath);
        var resultList = new List<bool>();
        fileList.ForEach(f => resultList.Add(false));

        {
            //Find scene guid.
            int iid = asset.GetInstanceID();
            string selectName = AssetDatabase.GetAssetPath(iid);
            if (AssetDatabase.IsMainAsset(iid))
            {
                FileStream stream = File.OpenRead(selectName + ".meta");
                TextReader reader = new StreamReader(stream);

                string line = null;
                string assetGUID = "";
                while ((line = reader.ReadLine()) != null)
                {
                    if (line.IndexOf("guid") != -1)
                    {
                        assetGUID = line.Replace("guid: ", "");
                        break;
                    }
                }

                reader.Close();
                stream.Close();

                int filePerTask = Mathf.FloorToInt(fileList.Count / taskCount) + 1;

                var taskList = new List<SearchGuidTask>();

                for (int i = 0; i < taskCount; i++)
                {
                    var task = new SearchGuidTask()
                    {
                        FileList = fileList,
                        ResultList = resultList,
                        TargetGuid = assetGUID,
                        StartIndex = i * filePerTask,
                        Count = filePerTask,
                    };
                    taskList.Add(task);
                }

                foreach (var itr in taskList)
                {
                    itr.Start();
                }

                foreach (var itr in taskList)
                {
                    itr.Join();
                }

                int counter = 0;
                string outPut = string.Empty;
                for (int i = 0; i < resultList.Count; i++)
                {
                    if (resultList[i])
                    {
                        outPut += fileList[i] + "\n;";
                        counter++;
                    }
                }

                string[] splits = selectName.Split("/".ToCharArray());
                outPut += "~~~Asset: " + splits[splits.Length - 1] + " is referred by " + counter + " asset(s): \n";
                outPut += "~~~End\n";
                Debug.Log(outPut);
            }
        }
    }

    private static void GetAllDirectory(string root, List<string> dirList) //参数dirPath为指定的目录
    {
        if (!dirList.Contains(root))
        {
            dirList.Add(root);
        }

        DirectoryInfo Dir = new DirectoryInfo(root);
        foreach (DirectoryInfo d in Dir.GetDirectories())
        {
            dirList.Add(d.ToString() + "\\");
            GetAllDirectory(d.ToString() + "\\", dirList);
        }
    }

    public static List<string> GetAllFiles(string root)
    {
        var dirList = new List<string>();
        GetAllDirectory(root, dirList);

        var taskList = new List<CollectFileTask>();
        int filePerTask = Mathf.FloorToInt(dirList.Count / taskCount) + 1;
        for (int i = 0; i < taskCount; i++)
        {
            var task = new CollectFileTask()
            {
                DirList = dirList,
                Count = filePerTask,
                StartIndex = i * filePerTask,
            };
            taskList.Add(task);
        }

        foreach (var itr in taskList)
        {
            itr.Start();
        }

        foreach (var itr in taskList)
        {
            itr.Join();
        }

        var fileList = new List<string>();
        foreach (var task in taskList)
        {
            fileList.AddRange(task.Files);
        }
        return fileList;
    }
}

public class CollectFileTask
{
    public List<string> DirList;
    public int StartIndex;
    public int Count;

    public List<string> Files = new List<string>();

    private Thread curThread;
    private const string pattern = "*.*";

    public void Start()
    {
        curThread = new Thread(Run);
        curThread.Start();
    }

    private void Run()
    {
        for (int i = StartIndex; i < StartIndex + Count; i++)
        {
            if (i >= DirList.Count)
            {
                return;
            }

            var dirPath = DirList[i];

            DirectoryInfo Dir = new DirectoryInfo(dirPath);
            foreach (FileInfo f in Dir.GetFiles(pattern))
            {
                Files.Add(f.ToString());
            }
        }
    }

    public void Join()
    {
        curThread?.Join();
    }
}

public class SearchGuidTask
{
    public List<string> FileList;
    public List<bool> ResultList;
    public int StartIndex;
    public int Count;
    public string TargetGuid;

    private Thread curThread;

    public void Start()
    {
        curThread = new Thread(Run);
        curThread.Start();
    }

    private void Run()
    {
        for (int i = StartIndex; i < StartIndex + Count; i++)
        {
            if (i >= FileList.Count)
            {
                return;
            }

            var path = FileList[i];
            if (String.IsNullOrEmpty(path))
            {
                continue;
            }

            if (path.IndexOf(".prefab") == -1 &&
                path.IndexOf(".mat") == -1 &&
                path.IndexOf("cs.meta") == -1 &&
                path.IndexOf(".anim") == -1 &&
                path.IndexOf(".controller") == -1 &&
                path.IndexOf(".renderTexture") == -1 &&
                path.IndexOf(".physicMaterial") == -1 &&
                path.IndexOf(".physicsMaterial2D") == -1 &&
                path.IndexOf(".overrideController") == -1 &&
                path.IndexOf(".mask") == -1 &&
                path.IndexOf(".cubemap") == -1 &&
                path.IndexOf(".flare") == -1 &&
                path.IndexOf(".fontsettings") == -1 &&
                path.IndexOf(".guiskin") == -1 &&
                path.IndexOf(".unity") == -1 &&
                path.IndexOf(".asset") == -1)//If not file that contains guid(static reference), continue.
            {
                continue;
            }

            string content = File.ReadAllText(path);
            if (!String.IsNullOrEmpty(content) && content.Contains(TargetGuid))
            {
                ResultList[i] = true;
            }
        }
    }

    public void Join()
    {
        curThread?.Join();
    }
}